# coding=utf8
__author__ = 'lijian'
import time
import setting
from utils import bcdUtil
from utils import byteUtil
import logging
import config
import event

import rtu
import random

_MAX_HISTORY = 1200

_EXCEPT_DETECTIONCOUNT = 1


def randomOffset(value):
    r = random.uniform(0.01, 0.08)
    number = value + value * r
    return number


class LampElectricInfo(object):
    def __init__(self):
        self.level = 0  # 亮度等级

        self.v = 0  # 电压
        self.a = 0  # 电流
        self.w = 0  # 有功
        self.pf = 0  # 功率因素
        self.ws = 0  # 用电量
        self.alarmStatue = 0  # 报警状态

        self.time = None


# 控制器
class Term(object):
    def __init__(self, deviceId, uid, enable, loopFlag, poleNo, sync=False, groups=None):
        self._deviceId = deviceId  # 设备序号
        self._uid = uid  # uid
        self._enable = enable  # 有效标志
        self._loopFlag = loopFlag  # 回路有效标志
        self._poleNo = poleNo  # 杆号

        self._holders = []
        self._sync = sync

        self.poolSuccess = False

        if not groups:
            self._groups = []
        else:
            self._groups = groups

    def addHolder(self, holder):
        holder.ownerTerm = self
        self._holders.append(holder)

    def getHolder(self, holderNo):
        for h in self._holders:
            if h.holderNo == holderNo:
                return h
        return None

    def addGroup(self, group):
        self._groups.append(group)

    def findHolderByLine(self, lineNo):
        for h in self._holders:
            if h.lineNo == lineNo:
                return h
        return None

    # level:0x01--完全初始化；0x02—保存当前运行数据；0x03—保留参数清除历史数据
    def reset(self, level):
        if level == 1:
            self._groups = []

        if level <= 2:
            self._autoTasks = []

        if level <= 3:
            for holder in self._holders:
                holder.clearHistory()


    @property
    def sync(self):
        return self._sync

    @sync.setter
    def sync(self, value):
        self._sync = value

    @property
    def holders(self):
        return tuple(self._holders)

    @property
    def deviceId(self):
        return self._deviceId

    @property
    def uid(self):
        return tuple(self._uid)

    @property
    def enable(self):
        return self._enable

    @property
    def loopFlag(self):
        return self._loopFlag

    @property
    def poleNo(self):
        return self._poleNo

    @property
    def groups(self):
        return tuple(self._groups)

    @property
    def uid(self):
        return self._uid

    @property
    def enable(self):
        return self._enable


# 灯具
class Holder(object):
    def __init__(self, holderNo, enable, lineNo, phase, loopNo):
        self._holderNo = holderNo  # 灯具序号
        self._enable = enable  # 启用标志
        self._lineNo = lineNo  # 出线系号
        self._phase = phase  # 相别
        self._loopNo = loopNo  # 回路号

        self._ownerTerm = None
        self._uid = None

        self._autoTasks = [None] * 10
        self._history = []
        self._lastHistoryTime = 0

        self.level = 0  # 亮度等级
        self.v = 0  # 电压
        self.a = 0  # 电流
        self.w = 0  # 有功
        self.pf = 0  # 功率因素
        self.ws = 0  # 用电量
        self.alarmStatue = 0  # 报警状态

        self.eventStatus = {
            "maxCurrent": {"status": False, "startCount": 0, "recoverCount": 0},
            "minCurrent": {"status": False, "startCount": 0, "recoverCount": 0},
            "lowPF": {"status": False, "startCount": 0, "recoverCount": 0},
            "breakdown": {"status": False, "startCount": 0, "recoverCount": 0},
            "burn": {"status": False, "startCount": 0, "recoverCount": 0},
        }

    def fixData(self, template):
        self.level = template.level
        self.v = randomOffset(template.v)
        self.a = randomOffset(template.a)
        self.w = randomOffset(template.w)
        self.pf = randomOffset(template.pf)
        self.ws = randomOffset(template.ws)
        # self.alarmStatue=0

    def clearData(self):
        self.level = 0
        self.v = 0
        self.a = 0
        self.w = 0
        self.pf = 0
        self.ws = 0

    def mockPowerOn(self):
        self.level=20
        self.v=randomOffset(222)
        self.a=randomOffset(1.71)
        self.w=randomOffset(267)
        self.pf=randomOffset(70.0)

    def mockPowerOff(self):
        self.clearData()

    @property
    def holderNo(self):
        return self._holderNo

    @property
    def enable(self):
        return self._enable

    @property
    def lineNo(self):
        return self._lineNo

    @property
    def phase(self):
        return self._phase

    @property
    def loopNo(self):
        return self._lineNo

    @property
    def ownerTerm(self):
        return self._ownerTerm

    @ownerTerm.setter
    def ownerTerm(self, value):
        self._ownerTerm = value
        uid = list(self._ownerTerm.uid)
        uid[0] = self._lineNo
        self._uid = tuple(uid)

    @property
    def uid(self):
        return self._uid

    @property
    def autoTasks(self):
        return tuple(self._autoTasks)

    def addAutoTasks(self, task):
        self._autoTasks[task.no - 1] = task

    def autoTasksSize(self):
        size = 0
        for task in self._autoTasks:
            if task:
                size += 1
        return size

    def findAutoTask(self, loopByte):
        for task in self._autoTasks:
            if task.getLoopByte() == loopByte:
                return task
        return None

    def onOffState(self):
        if self.level == 0:
            return 0
        elif self.level == 0x14:
            return 1
        else:
            return 2

    def runState(self):
        if self.alarmStatue == 0:
            return 0
        else:
            return 1

    def findHistory(self, startDateTime, m, count):
        details = []
        startTime = time.mktime(startDateTime.timetuple())

        for detail in self._history:
            if detail.time > startTime:
                details.append(detail)

                if len(details) >= count:
                    break

        return details

    def insertHistory(self):
        curTime = time.time()

        if curTime - self._lastHistoryTime <= setting.LAMP_HISTORY_INTERVAL:
            return

        detail = LampElectricInfo()
        detail.v = self.v
        detail.a = self.a
        detail.w = self.w
        detail.pf = self.pf
        detail.ws = self.ws
        detail.time = curTime

        self._history.append(detail)
        self._lastHistoryTime = curTime

        if len(self._history) > _MAX_HISTORY:
            self._history.remove(self._history[0])

    def clearHistory(self):
        self._history = []

    def checkException(self):
        self.checkPF()
        self.checkBurn()
        self.checkBreakdown()
        self.checkA()

    def checkA(self):
        # 电流过大检查
        if self.a > config.lampThresholdConfig["maxCurrentThreshold"]:
            self.eventStatus["maxCurrent"]["startCount"] += 1

            if self.eventStatus["maxCurrent"]["startCount"] >= _EXCEPT_DETECTIONCOUNT and not \
                    self.eventStatus["maxCurrent"]["status"]:
                self.eventStatus["maxCurrent"]["status"] = True
                self.eventStatus["maxCurrent"]["recoverCount"] = 0

                data = []
                data.append(1)  # 事件开始
                data.extend(byteUtil.encodeShort(self.lampNo))  # 灯号
                data.extend(byteUtil.encodeA2(self.a))
                erc = event.Event(code=19, data=data)
                event.addEvent(erc)

        elif self.a <= config.lampThresholdConfig["maxCurrentRecover"] and \
                self.eventStatus["maxCurrent"]["status"]:
            self.eventStatus["maxCurrent"]["recoverCount"] += 1

            if self.eventStatus["maxCurrent"]["recoverCount"] >= _EXCEPT_DETECTIONCOUNT:
                self.eventStatus["maxCurrent"]["status"] = False
                self.eventStatus["maxCurrent"]["startCount"] = 0

                data = []
                data.append(0)  # 事件恢复
                data.extend(byteUtil.encodeShort(self.lampNo))  # 灯号
                data.extend(byteUtil.encodeA2(self.a))
                erc = event.Event(code=19, data=data)
                event.addEvent(erc)



        # 电流过小检查
        if self.a < config.lampThresholdConfig["minCurrentThreshold"]:
            self.eventStatus["minCurrent"]["startCount"] += 1

            if self.eventStatus["minCurrent"]["startCount"] >= _EXCEPT_DETECTIONCOUNT and not \
                    self.eventStatus["minCurrent"]["status"]:
                self.eventStatus["minCurrent"]["status"] = True
                self.eventStatus["minCurrent"]["recoverCount"] = 0

                data = []
                data.append(1)  # 事件开始
                data.extend(byteUtil.encodeShort(self.lampNo))  # 灯号
                data.extend(byteUtil.encodeA2(self.a))
                erc = event.Event(code=20, data=data)
                event.addEvent(erc)

        elif self.a >= config.lampThresholdConfig["minCurrentRecover"] and \
                self.eventStatus["minCurrent"]["status"]:
            self.eventStatus["minCurrent"]["recoverCount"] += 1

            if self.eventStatus["minCurrent"]["recoverCount"] >= _EXCEPT_DETECTIONCOUNT:
                self.eventStatus["minCurrent"]["status"] = False
                self.eventStatus["minCurrent"]["startCount"] = 0

                data = []
                data.append(0)  # 事件恢复
                data.extend(byteUtil.encodeShort(self.lampNo))  # 灯号
                data.extend(byteUtil.encodeA2(self.a))
                erc = event.Event(code=20, data=data)
                event.addEvent(erc)

    def checkPF(self):
        if self.pf < config.lampThresholdConfig["lowPFThreshold"]:
            self.eventStatus["lowPF"]["startCount"] += 1

            if self.eventStatus["lowPF"]["startCount"] >= _EXCEPT_DETECTIONCOUNT and not \
                    self.eventStatus["lowPF"]["status"]:
                self.eventStatus["lowPF"]["status"] = True
                self.eventStatus["lowPF"]["recoverCount"] = 0

                data = []
                data.append(1)  # 事件开始
                data.extend(byteUtil.encodeShort(self.lampNo))  # 灯号
                data.extend(byteUtil.encodeA2(self.a))
                data.extend(byteUtil.encodeA5(self.pf))
                erc = event.Event(code=21, data=data)
                event.addEvent(erc)

        elif self.pf >= config.lampThresholdConfig["lowPFRecover"] and \
                self.eventStatus["lowPF"]["status"]:
            self.eventStatus["lowPF"]["recoverCount"] += 1

            if self.eventStatus["lowPF"]["recoverCount"] >= _EXCEPT_DETECTIONCOUNT:
                self.eventStatus["lowPF"]["status"] = False
                self.eventStatus["lowPF"]["startCount"] = 0

                data = []
                data.append(0)  # 事件开始
                data.extend(byteUtil.encodeShort(self.lampNo))  # 灯号
                data.extend(byteUtil.encodeA2(self.a))
                data.extend(byteUtil.encodeA5(self.pf))
                erc = event.Event(code=21, data=data)
                event.addEvent(erc)

    def checkBreakdown(self):
        if self.a < config.lampThresholdConfig["breakdownCurrentThreshold"]:
            self.eventStatus["breakdown"]["startCount"] += 1

            if self.eventStatus["breakdown"]["startCount"] >= _EXCEPT_DETECTIONCOUNT and not \
                    self.eventStatus["breakdown"]["status"]:
                self.eventStatus["breakdown"]["status"] = True
                self.eventStatus["breakdown"]["recoverCount"] = 0

                data = []
                data.append(1)  # 事件开始
                data.extend(byteUtil.encodeShort(self.lampNo))  # 灯号
                data.extend(byteUtil.encodeA2(self.v))
                data.extend(byteUtil.encodeA2(self.a))
                erc = event.Event(code=22, data=data)
                event.addEvent(erc)

        elif self.a >= config.lampThresholdConfig["breakdownCurrentRecover"] and \
                self.eventStatus["breakdown"]["status"]:
            self.eventStatus["breakdown"]["recoverCount"] += 1

            if self.eventStatus["breakdown"]["recoverCount"] >= _EXCEPT_DETECTIONCOUNT:
                self.eventStatus["breakdown"]["status"] = False
                self.eventStatus["breakdown"]["startCount"] = 0

                data = []
                data.append(0)  # 事件恢复
                data.extend(byteUtil.encodeShort(self.lampNo))  # 灯号
                data.extend(byteUtil.encodeA2(self.v))
                data.extend(byteUtil.encodeA2(self.a))
                erc = event.Event(code=22, data=data)
                event.addEvent(erc)

    def checkBurn(self):
        if self.a < config.lampThresholdConfig["burnCurrentThreshold"]:
            self.eventStatus["burn"]["startCount"] += 1

            if self.eventStatus["burn"]["startCount"] >= _EXCEPT_DETECTIONCOUNT and not \
                    self.eventStatus["burn"]["status"]:
                self.eventStatus["burn"]["status"] = True
                self.eventStatus["burn"]["recoverCount"] = 0

                data = []
                data.append(1)  # 事件开始
                data.extend(byteUtil.encodeShort(self.lampNo))  # 灯号
                data.extend(byteUtil.encodeA2(self.v))
                data.extend(byteUtil.encodeA2(self.a))
                erc = event.Event(code=23, data=data)
                event.addEvent(erc)

        elif self.a >= config.lampThresholdConfig["burnCurrentRecover"] and \
                self.eventStatus["burn"]["status"]:
            self.eventStatus["burn"]["recoverCount"] += 1

            if self.eventStatus["burn"]["recoverCount"] >= _EXCEPT_DETECTIONCOUNT:
                self.eventStatus["burn"]["status"] = False
                self.eventStatus["burn"]["startCount"] = 0

                data = []
                data.append(0)  # 事件恢复
                data.extend(byteUtil.encodeShort(self.lampNo))  # 灯号
                data.extend(byteUtil.encodeA2(self.v))
                data.extend(byteUtil.encodeA2(self.a))
                erc = event.Event(code=23, data=data)
                event.addEvent(erc)

    # 视在功率
    @property
    def APP(self):
        if self.pf == 0:
            return 0
        else:
            app = self.w / (self.pf / 100.0)
            return round(app, 3)

    # 无功功率
    @property
    def RP(self):
        return self.APP - self.w

    # 电压
    @staticmethod
    def encodeV(holder):
        return byteUtil.encodeA7(holder.v)

    # 电流
    @staticmethod
    def encodeA(holder):  # 0.36
        return byteUtil.encodeA25(holder.a)

    # 有功功率
    @staticmethod
    def encodeAP(holder):
        return byteUtil.encodeA2(holder.w)

    # 功率因素
    @staticmethod
    def encodePF(holder):
        bcd = byteUtil.encodeA5(holder.pf)
        return bcd

    # 视在功率
    @staticmethod
    def encodeAPP(holder):
        bcd = byteUtil.encodeA2(holder.APP)
        return bcd

    # 无功功率
    @staticmethod
    def encodeRP(holder):
        bcd = byteUtil.encodeA2(holder.RP)
        return bcd


class Group(object):
    def __init__(self, groupNo):
        self._groupNo = groupNo
        self._lamps = []
        self._autoTasks = [None] * 10

    def addTerm(self, term):
        self._lamps.append(term)

    def addAutoTasks(self, task):
        self._autoTasks[task.no - 1] = task

    def autoTasksSize(self):
        size = 0
        for task in self._autoTasks:
            if task:
                size += 1
        return size

    @property
    def groupNo(self):
        return self._groupNo

    @property
    def autoTasks(self):
        return tuple(self._autoTasks)

    @property
    def terms(self):
        return tuple(self._lamps)


# 自动控制任务
class AutoCtrlTask(object):
    def __init__(self):
        self.no = 0
        self.timeType = None
        self.ctrlType = None
        self.level = None
        self.absoluteTime = None
        self.relativeTime = None


class PlanTask(object):
    HOLDER_TASK = 0
    GROUP_TASK = 1
    TERM_TASK = 2

    def __init__(self, taskType):
        self.No = 0
        self.planTime = None
        self.optType = 0
        self.level = 0

        self.holderNo = 0
        self.groupNo = None
        self.deviceId = 0

        self.finished = False

        self.taskType = taskType
